#include "WxVolumeRenderingPicker.h"
#include <mitkProperties.h>
#include <mitkPointOperation.h>
#include <mitkAction.h>
#include <mitkPositionEvent.h>
#include <mitkInteractionConst.h>
#include <mitkStateEvent.h>
#include <mitkVtkPropRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include "WxDoNothingMapper.h"
#include <vtkVolumeMapper.h>
#include <vtkVolumeProperty.h>
#include <vtkTransform.h>
#include <vtkCamera.h>
#include <vtkImageData.h>
#include <vtkPiecewiseFunction.h>
#include <mitkTransferFunctionProperty.h>
#include <mitkTransferFunction.h>
#include <vtkVolume.h>

#include <vtkPolyDataMapper.h>
#include <vtkSphereSource.h>
#include <vtkProperty.h>

mitk::WxVolumeRenderingPicker::WxVolumeRenderingPicker(const char * type)
    : mitk::StateMachine(type), m_MaxOpacity(0.75),m_MinValue(0),m_Factor(1),m_Step(1)
{

}

mitk::WxVolumeRenderingPicker::~WxVolumeRenderingPicker()
{
}


bool mitk::WxVolumeRenderingPicker::ExecuteAction(mitk::Action* action, mitk::StateEvent const* stateEvent)
{
    mitk::Point3D eventPoint;
    const mitk::DisplayPositionEvent* displayPosEvent = dynamic_cast<const mitk::DisplayPositionEvent*>(stateEvent->GetEvent());
    mitk::VtkPropRenderer* sender = (mitk::VtkPropRenderer*) stateEvent->GetEvent()->GetSender();
    if((displayPosEvent == NULL) || (sender == NULL))
        return false;

    eventPoint[0] = displayPosEvent->GetDisplayPosition()[0];
    eventPoint[1] = displayPosEvent->GetDisplayPosition()[1];
    eventPoint[2] = 0;


    bool ok = false;
    if (action->GetActionId()==mitk::AcNEWPOINT)
    {
        vtkRenderWindow *renderWindow = NULL;
        vtkRenderWindowInteractor *renderWindowInteractor = NULL;

        mitk::VtkPropRenderer* renderer = (mitk::VtkPropRenderer*) stateEvent->GetEvent()->GetSender();

        if ( renderer != NULL&&renderer->GetMapperID()==mitk::BaseRenderer::Standard3D )
        {
            renderWindow = renderer->GetRenderWindow();
            if ( renderWindow != NULL )
            {
                renderWindowInteractor = renderWindow->GetInteractor();
            }

            renderWindowInteractor->Disable();
            int pixel[3];
            float pixposition[3];
            float positionValue;
            if(this->Get3DPixelUnder2DPosition(renderer->GetVtkRenderer(),eventPoint[0],eventPoint[1],pixel,pixposition,&positionValue))
            {
                Point3D p3d(pixposition);
                if(this->m_CrossBallNode.IsNotNull())
                {
                    this->m_CrossBallNode->GetData()->SetOrigin(p3d);
                    this->m_CrossBallNode->SetVisibility(true);
                }
            }
            else
            {
                if(this->m_CrossBallNode.IsNotNull())
                {
                    this->m_CrossBallNode->SetVisibility(false);
                }
            }

                mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetVtkRenderer()->GetRenderWindow());
                renderWindowInteractor->Enable();
                ok = true;


        }
    }
        return ok;
}

#define SMALL_FLOAT 0.00000001
#define SEGMENT_LIES_IN_PLANE 0
#define SEGMENT_IS_PARALLEL_TO_PLANE_NO_INTERSECTION 0
#define INTERSECT_POINT 1

int mitk::WxVolumeRenderingPicker::GetpixPosition(const float P0[3], const float P1[3],const float Pnormal[3],const float Ppoint[3], float resultPt[3] )
{
	float    u[ 3];
	float    w[ 3];
	u[ 0]  = P1[ 0] - P0[ 0];
	u[ 1]  = P1[ 1] - P0[ 1];
	u[ 2]  = P1[ 2] - P0[ 2];
	w[ 0] =  P0[ 0] - Ppoint[ 0];
	w[ 1] =  P0[ 1] - Ppoint[ 1];
	w[ 2] =  P0[ 2] - Ppoint[ 2];

	float     D = Pnormal[0]*u[0]+Pnormal[1]*u[1]+Pnormal[2]*u[2];
	float     N = -(Pnormal[0]*w[0]+Pnormal[1]*w[1]+Pnormal[2]*w[2]);

	if (fabs(D) < SMALL_FLOAT) 
	{          
		if (N == 0)                    
			return SEGMENT_LIES_IN_PLANE;
		else
			return SEGMENT_IS_PARALLEL_TO_PLANE_NO_INTERSECTION;                   
	}

	float sI = N / D;
	if (sI < 0 || sI > 1)
		return SEGMENT_IS_PARALLEL_TO_PLANE_NO_INTERSECTION;

	resultPt[ 0] = P0[ 0] + sI * u[ 0];		
	resultPt[ 1] = P0[ 1] + sI * u[ 1];
	resultPt[ 2] = P0[ 2] + sI * u[ 2];

	return INTERSECT_POINT;


}
#define X_STACK 0
#define Y_STACK 1
#define Z_STACK 2
bool mitk::WxVolumeRenderingPicker::Get3DPixelUnder2DPosition(vtkRenderer* renderer,float x,float y,int pixel[],float pixPosition[],float* positionValue)
{
    if(this->m_ImageDataNode==NULL) return false;
    float maxOpacity=m_MaxOpacity, minValue=m_MinValue,factor=m_Factor,step=m_Step;
    mitk::Image* mitkImage=dynamic_cast<mitk::Image*>(this->m_ImageDataNode->GetData());
    if(mitkImage==NULL) return false;
    vtkImageData* SrcVlm=mitkImage->GetVtkImageData();
    mitk::TransferFunctionProperty* transferFunctionProp = dynamic_cast<mitk::TransferFunctionProperty*>(this->m_ImageDataNode->GetProperty("TransferFunction"));
    if(transferFunctionProp==NULL) return false;
    vtkPiecewiseFunction* opacityTransferFunction = transferFunctionProp->GetValue()->GetScalarOpacityFunction();
    renderer->SetDisplayPoint( x, y, 0);
    renderer->DisplayToWorld();
    double worldPointClicked[3];
    renderer->GetWorldPoint(worldPointClicked);
    worldPointClicked[0] /= factor;
    worldPointClicked[1] /= factor;
    worldPointClicked[2] /= factor;

    vtkMatrix4x4 *ActorMatrix = this->m_ImageDataNode->GetData()->GetUpdatedGeometry()->GetVtkTransform()->GetMatrix();

    vtkTransform *Transform = vtkTransform::New();
    if(ActorMatrix)
    {
            Transform->SetMatrix(ActorMatrix);
            Transform->Push();
    }
//    vtkVolume *volume=vtkVolume::New();
//    volume->SetUserTransform(Transform);
//    volume->ComputeMatrix();
//    double volumePosition[3];
//    volume->GetPosition(volumePosition);
//    volumePosition[0] /= factor;
//    volumePosition[1] /= factor;
//    volumePosition[2] /= factor;

//    MITK_INFO<<"volumePosition" << volumePosition[0]<<" "<<volumePosition[1]<<" "<<volumePosition[2] ;
    // camera view plane normal
    vtkCamera* camera=renderer->GetActiveCamera();
    double cameraViewPlaneNormal[3];
    camera->GetViewPlaneNormal(cameraViewPlaneNormal);
    // camera position
    double cameraPosition[3];
    camera->GetPosition(cameraPosition);
    cameraPosition[0] /= factor;
    cameraPosition[1] /= factor;
    cameraPosition[2] /= factor;
    float o[9]={1,0,0,
		0,1,0,
		0,0,1};

    double cameraProjObj[3];
    cameraProjObj[0] =	cameraViewPlaneNormal[0] * o[0] + cameraViewPlaneNormal[1] * o[1] + cameraViewPlaneNormal[2] * o[2];
    cameraProjObj[1] =	cameraViewPlaneNormal[0] * o[3]	+ cameraViewPlaneNormal[1] * o[4] + cameraViewPlaneNormal[2] * o[5];
    cameraProjObj[2] =	cameraViewPlaneNormal[0] * o[6]	+ cameraViewPlaneNormal[1] * o[7] + cameraViewPlaneNormal[2] * o[8];
    long stackOrientation, stackMax;
    int* dimensions=SrcVlm->GetDimensions();
    int* extent=SrcVlm->GetExtent();
    short* pdata=(short*)SrcVlm->GetScalarPointer();
    if( fabs(cameraProjObj[0]) > fabs(cameraProjObj[1]) && fabs(cameraProjObj[0]) > fabs(cameraProjObj[2]))
    {
        stackOrientation = X_STACK;
        stackMax=extent[1]-extent[0]+1;
        stackMax*=2;

    }
    else if( fabs(cameraProjObj[1]) > fabs(cameraProjObj[0]) && fabs(cameraProjObj[1]) > fabs(cameraProjObj[2]))
    {
        stackOrientation = Y_STACK;
        stackMax=extent[3]-extent[2]+1;
        stackMax*=2;

    }
    else
    {
        stackOrientation = Z_STACK;
        stackMax=extent[5]-extent[4]+1;
        stackMax*=2;

    }
    if(camera->GetParallelProjection())
    {
        cameraPosition[0] = worldPointClicked[0] + cameraViewPlaneNormal[0];
        cameraPosition[1] = worldPointClicked[1] + cameraViewPlaneNormal[1];
        cameraPosition[2] = worldPointClicked[2] + cameraViewPlaneNormal[2];
    }
    //ոͷ
    float	point1[3], point2[3];
    point1[0] = cameraPosition[0];
    point1[1] = cameraPosition[1];
    point1[2] = cameraPosition[2];
    // Զ....
    point2[0] = cameraPosition[0] + (worldPointClicked[0] - cameraPosition[0])*1500;
    point2[1] = cameraPosition[1] + (worldPointClicked[1] - cameraPosition[1])*1500;
    point2[2] = cameraPosition[2] + (worldPointClicked[2] - cameraPosition[2])*1500;

    // volume position

    bool direction;
    switch(stackOrientation)
    {
    case X_STACK:
        if( point1[0] - point2[0] < 0) direction = true;
        else direction = false;
        break;
    case Y_STACK:
        if( point1[1] - point2[1] < 0) direction = true;
        else direction = false;
        break;
    case Z_STACK:
        if( point1[2] - point2[2] < 0) direction = true;
        else direction = false;
        break;
    }

    double p, n;
    bool pointFound = false;
    float opacitySum = 0.0;


   // double *Spacing=SrcVlm->GetSpacing();

    for( p = 0; p < stackMax; p+=step)
    {
        n = (direction)? p : (stackMax-1)-p;

        float currentPoint[3], planeVector[3];
        switch(stackOrientation)
        {
        case X_STACK:
            currentPoint[0] = n ;
            currentPoint[1] = 0;
            currentPoint[2] = 0;

            planeVector[0] = o[0];
            planeVector[1] = o[1];
            planeVector[2] = o[2];
            break;

        case Y_STACK:
            currentPoint[0] = 0;
            currentPoint[1] = n ;
            currentPoint[2] = 0;

            planeVector[0] = o[3];
            planeVector[1] = o[4];
            planeVector[2] = o[5];
            break;

        case Z_STACK:
            currentPoint[0] = 0;
            currentPoint[1] = 0;
            currentPoint[2] = n ;

            planeVector[0] = o[6];
            planeVector[1] = o[7];
            planeVector[2] = o[8];
            break;
        }
//        double *PointOrigin=SrcVlm->GetOrigin();
//        currentPoint[0] += volumePosition[0];
//        currentPoint[1] += volumePosition[1];
//        currentPoint[2] += volumePosition[2];

//        currentPoint[0] += PointOrigin[0];
//        currentPoint[1] += PointOrigin[1];
//        currentPoint[2] += PointOrigin[2];
        Transform->TransformPoint(currentPoint,currentPoint);

        float resultPt[3];

        if( GetpixPosition(point1, point2, planeVector, currentPoint, resultPt))
        {

            float tempPoint3D[3];

            Transform->Inverse();
            Transform->TransformPoint(resultPt,tempPoint3D);
            Transform->Inverse();


//            tempPoint3D[0] -= volumePosition[0];
//            tempPoint3D[1] -= volumePosition[1];
//            tempPoint3D[2] -= volumePosition[2];

//            tempPoint3D[0] -= PointOrigin[0];
//            tempPoint3D[1] -= PointOrigin[1];
//            tempPoint3D[2] -= PointOrigin[2];

//            tempPoint3D[0] /= Spacing[0];
//            tempPoint3D[1] /= Spacing[1];
//            tempPoint3D[2] /= Spacing[2];

            long ptInt[3];

            ptInt[0] = (long) (tempPoint3D[0] + 0.5);
            ptInt[1] = (long) (tempPoint3D[1] + 0.5);
            ptInt[2] = (long) (tempPoint3D[2] + 0.5);

            long currentSliceNumber, xPosition, yPosition;
            //float *imagePoint;
            short *Slice;
            int SliceSize=dimensions[0]*dimensions[1];
            float currentPointValue;
            currentSliceNumber = ptInt[2];
//            MITK_INFO<<"ptint" << ptInt[0]<<" "<<ptInt[1]<<" "<<ptInt[2] ;
//            MITK_INFO<<"ext" << extent[1]<<" "<<extent[3]<<" "<<extent[5] ;
//            MITK_INFO<<"Spacing" << Spacing[0]<<" "<<Spacing[1]<<" "<<Spacing[2] ;

            ////////////////球//////////////////
//            if (1)
//            {

//                vtkPolyDataMapper *sphereMapper;
//                vtkSphereSource *sphere;
//                sphere = vtkSphereSource::New();
//                sphereMapper = vtkPolyDataMapper::New();

//                sphere->SetRadius(5);
//                sphere->Update();
//                sphereMapper->SetInputConnection(sphere->GetOutputPort());

//                vtkActor *newshp=vtkActor::New();
//                newshp->SetMapper(sphereMapper);
//                double k=p/(double)stackMax;
//                newshp->GetProperty()->SetColor(1-k,k,0.5);
//                newshp->GetProperty()->SetAmbient(0.7);
//                newshp->GetProperty()->SetDiffuse(0.0);
//                newshp->GetProperty()->SetSpecular(0.0);

//                //newshp->AddPosition(resultPt[0],resultPt[1],resultPt[2]);
//                newshp->AddPosition(currentPoint[0],currentPoint[1],currentPoint[2]);
//                renderer->AddActor(newshp);

//                }
            if( ptInt[0] >= 0 && ptInt[0] <=extent[1] && ptInt[1] >= 0 && ptInt[1] <=extent[3] &&  ptInt[ 2] >= 0 && ptInt[ 2] <=extent[5])
            {
                Slice=pdata+currentSliceNumber*SliceSize;
                xPosition = ptInt[0];
                yPosition = ptInt[1];

                currentPointValue = Slice[xPosition+yPosition*dimensions[1]];
                opacitySum += opacityTransferFunction->GetValue( currentPointValue);
                MITK_INFO<<currentPointValue<<"  "<<opacitySum;
                    if( minValue)
                    {
                        pointFound = currentPointValue >= minValue;
                        pointFound = pointFound && opacitySum >= maxOpacity;
                    }
                    else
                        pointFound = opacitySum >= maxOpacity;

                    if( pointFound)
                    {
                        *positionValue = currentPointValue;
                        pixel[ 0] = ptInt[ 0];
                        pixel[ 1] = ptInt[ 1];
                        pixel[ 2] = ptInt[ 2];

                        pixPosition[ 0] = resultPt[0];
                        pixPosition[ 1] = resultPt[1];
                        pixPosition[ 2] = resultPt[2];

                        p = stackMax;	// stop the 'for loop'


                    }

            }
        }
    }
    Transform->Delete();
    return pointFound;
}
